package site.shenjie.hystrix.demo.command;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolProperties;

/**
 * 沈杰
 * 2020-7-27 15:24:40
 * 使用Hystrix必须有一个继承子HystrixCommand的子类，必须实现抽象方法：run
 */
public class MyHystrixCommand extends HystrixCommand {
    private final String name;

    public MyHystrixCommand(String name) {
//        // Hystrix 的简单使用
//        super(HystrixCommandGroupKey.Factory.asKey("MyGroup"));

//        // 信号量隔离策略配置
//        super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyGroup"))
//                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
//                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE
//                        )));

        /**
         * 线程隔离策略配置
         *
         * 执行依赖代码的线程与请求线程（比如 Tomcat 线程）分离，
         * 请求线程可以自由控制离开的时间，
         * 这也是我们通常说的异步编程，
         * Hystrix 是结合 RxJava 来实现的异步编程。
         *
         * 线程隔离策略的优点如下：
         * · 一个依赖调用可以给予一个线程池，这个依赖的异常不会影响其他的依赖。
         * · 使用线程可以完全隔离业务代码，请求线程可以快速返回。
         * · 可以完全模拟异步调用，方便异步编程。
         * 线程隔离策略的缺点：
         * · 使用线程池的缺点主要是增加了计算的开销。每一个依赖调用都会涉及到队列，调度，上下文切换，而这些操作都有可能在不同的线程中执行。
         */
        super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyGroup"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD))
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10)
                        .withMaxQueueSize(100).withMaximumSize(100)));

        this.name = name;
    }

    /**
     * 继承所必须重写的方法
     */
    @Override
    protected Object run() throws Exception {
//        // 故意睡眠1秒钟，用于触发调用失败时的回退
//        Thread.sleep(1000 * 10);
        /**
         * 在 run 方法中特意输出了线程名称，通过这个名称就可以确定当前是 线程隔离 还是 信号量隔离 。
         * 如果采用了信号量隔离策略会打印和返回name+信号量名称
         * 如果采用了线程隔离策略则会打印和返回name+线程名称
         */
//        System.out.println(this.name + ":" + Thread.currentThread().getName());

        /**
         * 为了证明能够用到缓存，在 run 方法中加一行输出，
         * 会观察到：多次调用时，每次都有返回，但是run里面打印的次数只有一次
         * 这个现象反映了多次调用时，只有第一次调用获取返回值，其他几次则是未进行真实地调用，而是直接读取之前的缓存
         */
        System.out.println("get data");

        return this.name + ":" + Thread.currentThread().getName();
    }

    /**
     * 调用失败时，执行：getFallback 方法
     * 返回回退内容
     */
    @Override
    protected String getFallback() {
        return "失败了 ";
    }

    /**
     * 在 Hystrix 中也为我们提供了方法级别的缓存。
     * 通过重写 getCacheKey 来判断是否返回缓存的数据，
     * getCacheKey 可以根据参数来生成。
     * 这样，同样的参数就可以都用到缓存了。
     * <p>
     * 把创建对象时传进来的 name 参数作为缓存的 key。
     */
    @Override
    protected String getCacheKey() {
        return String.valueOf(this.name);
    }
}
